package com.inspinia.common.limit;

import com.inspinia.base.util.ContainerUtil;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

/**
 * 限流拦截器，需要配置
 * @author Veblen
 */
public class AccessLimitInterceptor extends HandlerInterceptorAdapter {

    @Resource
    RedisTemplate<String,Integer> redisTemplate;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod)handler;
            Method method = handlerMethod.getMethod();

            //如果没有accessLimit注解直接放行
            boolean annotationPresent = method.isAnnotationPresent(AccessLimit.class);
            if (!annotationPresent) {
                return true;
            }

            AccessLimit accessLimit = method.getAnnotation(AccessLimit.class);
            if (accessLimit == null) {
                return true;
            }

            String key = request.getRemoteHost() + request.getRequestURL();
            //获取redis存储次数
            Integer maxLimit = redisTemplate.opsForValue().get(key);

            //注解的限制时间
            int sec = accessLimit.sec();
            //注解的限流次数
            int limit = accessLimit.limit();
            //设置一个key和过期时间
            if (maxLimit == null){
                redisTemplate.opsForValue().set(key,1,sec,TimeUnit.SECONDS);
            }else if (maxLimit < limit){
                //比次数小每次加1
                redisTemplate.opsForValue().set(key,maxLimit + 1,sec,TimeUnit.SECONDS);
            }else {
                ContainerUtil.outputString(response,"请求过于频繁，请"+ sec +"秒稍后再试");
                return false;
            }
        }
        return true;
    }
}
